Function Pop-Message(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$false)]
        [string]$this_user='aaadmin',
        [string]$message="Web site auto login will be started in 10s. Please do not take any actions until another message is poped up!",
        [int]$seconds=10
    )
    If( (quser $this_user 2>$null) -imatch 'active' ){
        msg $this_user /time:$seconds /v /w $message 1>$null 2>>$log_file
    }
}
Function New-FileOrFolder($p,[Switch]$d){
  if(!(test-path $p -ea 0)){if($d){new-item -ItemType Directory -path $p -Force -ea 0|out-null}else{new-item -ItemType File -path $p -Force -ea 0|out-null}}
}
Function Set-FileAccess(){ # this function is used to add given user access to the given path
    param(
    	[Parameter(Mandatory=$True)]     
    	[string]$path,                   # the given path to add access
    	[Parameter(Mandatory=$True)]     
    	[string]$user,                   # the given user to be added access to the given path
    	[Parameter(Mandatory=$False)]    
    	[string]$access="Modify"         # the access type to be added, default is "Modify"
    )
	$Acl = (Get-Item $path).GetAccessControl('Access')
	$InheritanceFlags="None" ; $PropagationFlags="None"
	If( (Get-Item $path).psiscontainer){
	    $InheritanceFlags="ObjectInherit"
	}
	$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($user, $access,$InheritanceFlags, $PropagationFlags, 'Allow')
	$Acl.SetAccessRule($Ar)
	Set-Acl -path $path -AclObject $Acl
}
Function Write-Log(){
    [CmdletBinding()]param(
		[Parameter(Mandatory=$true)]
		[ValidateSet("Warning", "Error", "Success","Normal")]
		$log_type,
		$log_info,
		$log_file
	)
	$cnt=0
	Do{
	    Try{(get-date).tostring("yyyy-MM-dd HH:mm:ss")+"--"+$log_type+": "+$log_info |Out-File -FilePath $log_file -Append -Encoding utf8 -Force -ea 1;$rst=$true}Catch{$rst=$false
		sleep 1 ;$cnt++; if($cnt -gt 5){write-error "wait too long(5s) to write log";return}
		}Finally{}
	}While( !$rst )
}
Function Validate-Credential(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [ValidatePattern("^(nam|apac|eur)\\.{7,9}")]
        [string]$fid,
        [Parameter(Mandatory=$False)]
        [string]$pwd,
        [string]$log_file="$home\Documents\WindowsPowerShell\log.txt"
    )
    $sec_pwd=ConvertTo-SecureString $pwd -AsPlainText -Force
    $creds = New-Object System.Management.Automation.PSCredential ($fid, $sec_pwd)
    try{
        start-process -Credential $creds -FilePath ping -WindowStyle Hidden -ea 1
        return $true
    } catch {
        write-log Error $_.Exception.Message $log_file ; return $false
    }
}
Function Get-NewIEProcess($old_ie_process){
    If( $old_ie_process -eq $null ){
        return (ps iexplore -ea 0)
    }
    return (ps iexplore -ea 0|?{$_.id -notin $old_ie_process.id})
}
Function Get-CurrentMainIE($url){
    Foreach( $i in (New-Object -ComObject Shell.Application).Windows()){
	    If($i.Name  -like "*Internet Explorer*" -and $i.Visible -and $i.LocationURL -ieq $url){
	       return $i
	    }
	}
    return $null
}
Function Get-IEObject([AllowNull()]$ie_position){ # to be updated
    # $ie=$null
    # Foreach( $i in (New-Object -ComObject Shell.Application).Windows()){
	    # If($i.Name  -like "*Internet Explorer*" -and $i.Visible){
	       # $ie=$i
	    # }
	# }
    # If( $ie -eq $null ){
        $loop_cnt=0
        Do{
            Try{ # if we run below command immediately after we close ie process, it will give errors
                $ie=new-object -com InternetExplorer.Application ; break
            }Catch{
                If( $loop_cnt -gt 9 ){
                    write-log error $_ $log_file
                    return $null
                }
            }Finally{sleep 1 ; $loop_cnt++}
        }While( $true )
    # }
    $left=0;$top=0;$width,$height=$screen.Width,$screen.Height
    If( $ie_position -ne $null ){$width,$height = [math]::Truncate( $width/2),[math]::Truncate( $height/2)}
    Switch ($ie_position){
        1 {break}
        2 {$left=$width;$top=0;break}
        3 {$left=0;$top=$height;break}
        4 {$left=$width;$top=$height}
    }
    $ie.width,$ie.height=$width,$height
    $ie.Top =  $top ;   $ie.Left = $left
    $ie.visible = $true
	return $ie
}
Function Show-Window(){
    # somehow the ie window won't show up after we start ie process, this isused to bring it in front
    
    # to be optimized so the opened IE can be forced to be on top most as we found there is other scheduled powershel script running
    # and when that powershell script runs, its window will comes to the front, which will affect this script as it uses sendkey method
    [CmdletBinding()]param(
        [Parameter(Mandatory=$False,ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [int]$hwnd = (Get-NewIEProcess $old_ie_process|?{$_.MainWindowTitle -ne ""}).MainWindowHandle
    )
    $sig = '[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);'
    Add-Type -MemberDefinition $sig -name NativeMethods -namespace Win32
    [Win32.NativeMethods]::ShowWindowAsync($hwnd, 2) 1>$nul ;sleep 1 # Minimize window
    [Win32.NativeMethods]::ShowWindowAsync($hwnd, 4) 1>$nul # Restore window
}
Function Get-IETagValue(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [__ComObject]$target,
        [Parameter(Mandatory=$False)]
        [string]$method="getElementsByTagName",
        [string]$method_args="body",
        [string]$tag_property='innertext'
    )
    Try{ # $ie.Document.getElementById no longer support in version 8+ IE
        return ([System.__ComObject].InvokeMember($method,[System.Reflection.BindingFlags]::InvokeMethod,$null,$target,$method_args)|select $tag_property).$tag_property
    }Catch{
        return $null
    }Finally{}
}
Function Wait-LoginWindow(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [string]$url_type,                      #  cr, cr-lb, vaultstatus ....
        [string]$expectedContent,               # the string to match to the ie body to tell if the webpage loads successfully as expected
        [string]$screen_save_for_success_file,  # save for the success login website page
        [string]$screen_save_for_error_file,
        [string]$url,
        [int]$ie_position
    )
    Write-Log Normal "Waiting $url_type login popup window appear" $log_file ; $loop_cnt=0 ; sleep 5
    Do{
        Try{
            If( ($main_ie=Get-CurrentMainIE $url) -ne $null){
                If(($ie_body=Get-IETagValue $main_ie.Document) -ne $null){ # the page loads directly without login window popup
                    If( $ie_body -imatch $expectedContent ){
                        Exit-Script 'SUCCESS' "$url_type webpage loaded successfully without credentilas enter" $screen_save_for_success_file $ie_position
                        return $false
                    }
                }
            }
            If( $loop_cnt -gt 59 ){ # exit if script running time exceeds 60s
                Exit-Script 'ERROR' "Cannot find credential enter window or full webpage after 60s, maybe it's stucked." $screen_save_for_error_file $ie_position; exit
            }
        }Catch{}Finally{$loop_cnt++;sleep 1}
        Try{
            [Microsoft.VisualBasic.Interaction]::AppActivate('Windows Security') ; return $true
        }Catch{}Finally{}
    }While( $loop_cnt -lt 59)
    return $true
}
Function Get-LoginUser(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$False)]
        [boolean]$is_enduser_provided=$false,
        [ValidatePattern("^(nam|apac|eur)\\.{7,9}")]
        [string]$loginuser
    )
    If( !$is_enduser_provided){ # if $web_login_user has not beed provided, using default one(aaadmin(_p))
        $login_user_domain="nam"
        $login_user= Switch -regex ($this_machine.Substring(7,3)){ # check computer name to tell the env
            "p2n|c2n|e2n"{"aaadmin_p";break} # PROD,COB,PTE
            default{"aaadmin"}
        }
    }else{
        If( [string]::IsNullOrWhiteSpace($loginuser)){ # exit script
            Stop-WithError "User `'$loginuser`' is not valid(either null or empty)"
        }else{
            $login_user_domain,$login_user=$loginuser.split('\')
        }
    }
    return $login_user_domain,$login_user
}
Function Get-FIDPasswordFromTPAM(){
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$True)]
        [ValidatePattern("^(nam|apac|eur)\\.{7,9}")]
        [string]$fid,
        [Parameter(Mandatory=$True)]
        [string]$tpam_certificate_password,
        [Parameter(Mandatory=$False)]
        [string]$end_point_url='https://app2app-prod25.nsroot.net/HandlePWRequestService/HandlePWRequest',
        [Parameter(Mandatory=$False)]
        [string]$tpam_certificate="tpam_key.pfx"
    )
    $this_path=$MyInvocation.PSScriptRoot
    If( test-path "$this_path\$tpam_certificate" -and test-path "$this_path\tpam_client.dll" ){
        $fid_domain,$fid_account=$fid.split('\')
        [void][Reflection.Assembly]::LoadFrom("$this_path\tpam_client.dll") 
        $tpam_class = New-Object TPAMClient.Program
        Try{
            $tpam_password = $tpam_class.getPassword($end_point_url,"$this_path\$tpam_certificate",$tpam_certificate_password,$fid_domain,$fid_account)
            If( $tpam_password -imatch "^password" ){
                return $tpam_password.split(':')[-1].trim()
            }else{return $false}
        }Catch{
            Write-Log Normal $_ $log_file
            return $false
        }Finally{}
    }else{
        Write-Log Error 'One or more of (tpam_key.pfx,tpam_client.dll) is not found in $this_path' $log_file
        return $false
    }
}
Function Send-Creds(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [string]$user,                 # user name to send to the window
        [string]$pwd,                  # user password to send to the window
        [string]$screen_image_file,    # image used to capture the login window
        [int]$ie_position
    )
    [System.Windows.Forms.SendKeys]::SendWait("$user")  ; sleep 2
    [System.Windows.Forms.SendKeys]::SendWait("{TAB}$pwd{TAB}")
    Write-Log Normal "Trying to save login window screenshot" $log_file
    Save-Screenshot $screen_image_file -given_size $ie_position # save current screen shot
    [System.Windows.Forms.SendKeys]::SendWait("{ENTER}") ; sleep 5
}
Function Wait-WebpageLoad(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [string]$url_type, 
        [string]$expectedContent,
        [string]$screen_save_for_success_file,   # save for the success login website page
        [string]$screen_save_for_error_file,     # save for the failure login website page
        [string]$url,     # save for the failure login website page
        [int]$ie_position
    )
    write-log Normal "Waiting webpag loading until found expected content or timeout" $log_file ; $loop_cnt=0 ; sleep 5
    Do{ 
        Try{
            If( ($main_ie=Get-CurrentMainIE $url) -ne $null){
                If(( $ie_body=Get-IETagValue $main_ie.Document) -ne $null){
                    If( $ie_body -imatch $expectedContent ){
                        Exit-Script 'SUCCESS' "$url_type Website has been successfully loaded!" $screen_save_for_success_file $ie_position;return
                    }elseif( $ie_body -imatch 'HTTP Error 401.2 - Unauthorized'){
                        Exit-Script 'ERROR' "$url_type Website Error: HTTP Error 401.2 - Unauthorized" $screen_save_for_error_file $ie_position; return
                    }
                }
            }
            If( $loop_cnt -gt 59 ){
                Exit-Script 'ERROR' "$url_type Website is still loading after 60s" $screen_save_for_error_file $ie_position
                (gwmi win32_process|?{$_.name -imatch 'powershell'}).CommandLine|Out-File -FilePath $log_file -Append -Encoding utf8 -Force -ea 1 ; exit
            }
        }Catch{}Finally{$loop_cnt++ ; sleep 1}
    }While( $loop_cnt -le 60 )
}
Function Save-Screenshot(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [string]$save_file,
        [Parameter(Mandatory=$False)]
        [switch]$given_size,
        [AllowNull()][int]$ie_position # 1:left-top 2:right-top 3:left-bottom 4:right-bottom
    )
    $left=0;$top=0
    $width,$height=$screen.Width,$screen.Height
    If( $given_size ){
        If( $ie_position -ne $null ){$width,$height = [math]::Truncate( $width/2),[math]::Truncate( $height/2)}
        Switch ($ie_position){
            1 {break}
            2 {$left=$width;$width=$width*2;break}
            3 {$top=$height;$height=$height*2;break}
            4 {$left=$width;$top=$height;$width=$width*2;$height=$height*2}
        }
        
    }
    #in nam lab node1 got this error: <Method invocation failed because [System.Drawing.Rectangle] does not contain a method named 'new'.>
    # $bounds = [Drawing.Rectangle]::new($left, $top, $width, $height)
    Try{
        $bounds = [Drawing.Rectangle]::FromLTRB($left, $top, $width, $height)
        $bmp = New-Object Drawing.Bitmap $bounds.Width, $bounds.Height
        $graphics = [Drawing.Graphics]::FromImage($bmp)
        $graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size)
        $bmp.Save($save_file) 1>$null
        $graphics.Dispose() 1>$null
        $bmp.Dispose() 1>$null  
    }Catch{
        write-log Error $_.Exception.Message $log_file
    }Finally{}
}
Function Export-LogToAutosys([string]$autosys_log_file="C:\Program Files\CA\Workload Automation AE\SystemAgent\WA1_AGENT\log\WebsiteValidate\out.log"){
	Try{
		get-content $log_file|Add-Content $autosys_log_file -Force -Encoding UTF8
	}Catch{
	    write-log Error $_ $log_file
	}Finally{}
}
Function Send-Mailtoteam( ){ # this function is used to send email to notify team after the script finished.
	[CmdletBinding()]param(
		[Parameter(Mandatory=$True)]    
		[string]$taskRunResult,         # the result og script running: SUCCESS, FAILED, WARNING
		[string[]]$attach_files         # the fies to be send to team
	)
    $this_env="[$this_env-$this_domain-$this_node]".toupper()
	Try{ # cw50406@imcnam.ssmb.com  dl.gt.cn.cstc.rpa.aa.ste@imcnam.ssmb.com
		Send-MailMessage `
            -From ($this_user +"."+$this_machine+"@imcnam.ssmb.com") `
            -To dl.gt.cn.cstc.rpa.aa.ste@imcnam.ssmb.com `
            -Subject "$this_env RPA AA STE CR Website Login - $taskRunResult" `
            -Body "Hi RPA AA STE Support, `n`nCR Website Login has been taken action in $this_machine.`n`nPlease check details in the attached file. `n`nThank You! `n`n$this_user" `
            -Attachments $attach_files `
            -SmtpServer "imta.citicorp.com" -Port 25 `
            -Encoding([System.Text.Encoding]::UTF8) -ea 1
       Write-Log Success 'The notify email has been sent to RPA AA STE team.' $log_file
	}Catch{
	    Write-Log Error $_ $log_file
	}Finally{}
}
Function Exit-Script(){
    [CmdletBinding()]param(
        [Parameter(Mandatory=$True)]
        [string]$log_type,                       # normal success warning error
        [string]$log_info,                       # pass to function Send-Mailtoteam
        [string]$screenshot_file,                # pass to Save-Screenshot
        [int]$ie_position
    )
    Write-Log $log_type $log_info $log_file
    Save-Screenshot $screenshot_file -given_size $ie_position # save current screen shot
    If( $log_type -eq "error" ){(ps iexplore -ea 0|select id,name,MainWindowTitle|fl) |Out-File -FilePath $log_file -Append -Encoding utf8 -Force}
}
Function Stop-WithError(){ # this function is used to quit script when there is error with script running
    param(
		[Parameter(Mandatory=$True)]
    	[string]$error_detail
    )
	Write-Log Error $error_detail $log_file
    Export-LogToAutosys $autosys_out_log
	Send-Mailtoteam 'ERROR' $log_file
    ps -id $pid|kill -force
}
#<-----------------------------------------------------------------------------------load .NET assembles
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
#<-----------------------------------------------------------------------------------initilize parames 
$this_path = Split-Path -Parent ($MyInvocation.MyCommand.Definition)                 # this script running directory
[void][System.Reflection.Assembly]::Load([io.file]::ReadAllBytes("$this_path\CredentialValidate.dll"))
$this_domain=(Get-WmiObject Win32_ComputerSystem).Domain.Split(".")[0]               # this computer domain
$this_machine=$env:computername                                                      # running script computer
$this_user=$env:username                                                             # running script user
$this_user_pwd=[CredentialValidate.GetPassword]::ReturnLoginPWD()                    # this user password
$found_login_user=$false
$log_path = $this_path.Replace('\scripts\','\logs\')                                 # set log path
New-FileOrFolder $log_path -d                                                        # create log path if it doesn't exist
$log_file="$log_path\{0:yyyyMMdd_HHmmss}_CRWebsiteLogin.log" -f (get-date)           # log file to store running info
$this_env=Switch -regex ($this_machine.Substring(7,3).ToLower()){                    # this computer environment
    "p2n"     {"prod";break}
    "u2n"     {"uat" ;break}
    "-f2|-18" {"dev" ;break}
    "-44|-97" {"poc" ;break}
    "-f3"     {"lab" ;break}
    default{Write-Log Error "Please only run me in VM controlroom servers, not in this computer $this_machine" $log_file;exit}
}
$this_node=Switch -regex ($this_machine.Substring($this_machine.Length-2)){          # this computer node, we only check load balance in node 1
    "01|49|E2|9a"   {"1";break}
    "02|b8|0e|ae"   {"2"}
}
$cr_url="http://$this_machine.$this_domain.nsroot.net/controlroom/"                  # controlroom site url
$cr_lb_url=Switch -regex ($this_env){                                                # controlroom load balance site url
    "poc|lab"{"http://icg-aa-cr-$this_env-lb.$this_domain.nsroot.net/controlroom/";break}
    default  {"http://icg-aa-cr-$this_env.$this_domain.nsroot.net/controlroom/"         }
}
If( $this_domain -ine 'nam'){
    $cr_lb_url=$cr_lb_url.replace('-cr-',"-cr-$this_domain-")
}
$cr_vault_status_url=$cr_url.Replace('controlroom/','webcrsvc/vault/status')         # vault status site url
$cr_vault_status_lb_url=$cr_lb_url.Replace('controlroom/','webcrsvc/vault/status')   # vault status load balance site url
del "$this_path\*.png" -force -ea 0                                                  # delete old images
$screen = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds|select Width,Height    # screen Width Height
$autosys_log_path="C:\Program Files\CA\Workload Automation AE\SystemAgent\WA1_AGENT\log\WebsiteValidate"   # initilize autosys log path
$autosys_out_log="$autosys_log_path\out.log"                                                               # normal log file path and 
$autosys_err_log="$autosys_log_path\err.log"                                                               # error log file path
$old_ie_process =ps iexplore -ea 0